package com.xishu.service.order.callback;

import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.config.ContextConstant;
import com.xishu.entity.Company;
import com.xishu.entity.LockOrderCashOrOrderItem;
import com.xishu.entity.customer.*;
import com.xishu.entity.hardware.Printer;
import com.xishu.entity.oplog.OrderOpLog;
import com.xishu.entity.order.*;
import com.xishu.entity.shop.*;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.*;
import com.xishu.service.order.base.OrderService;
import com.xishu.service.print.PrinterService;
import com.xishu.socket.SocketMessage;
import com.xishu.socket.WebSocket;
import com.xishu.socket.WebSocketService;
import com.xishu.util.ClassUtil;
import com.xishu.util.DateUtil;
import com.xishu.util.ServletUtil;
import com.xishu.util.Tools;
import net.sf.json.JSONObject;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.impl.ContextBase;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.STOCK_NOT_ENOUGH;

public class OrderCallBackChain extends ChainBase {
    private static Logger logger = LoggerFactory.getLogger(OrderCallBackChain.class);
    private LockService localLockService = LockService.getInstance();
    private OrderService orderService = OrderService.getInstance();

    public OrderCallBackChain() {

    }

    public void callback(Order order) throws Exception {
        String lockKey = orderService.getLockKey(order.getShopId(), order.getParentOrderId());
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            try {
                OrderCallBackChain orderCallBackChain = new OrderCallBackChain();
                orderCallBackChain.addCommand(new VerifyStatusCmd());
                orderCallBackChain.addCommand(new InitCmd());
                orderCallBackChain.addCommand(new ExpireOrderCmd());
                orderCallBackChain.addCommand(new StartCmd());
                orderCallBackChain.addCommand(new SubOrderExistCmd());
                orderCallBackChain.addCommand(new SubOrderNoneExistCmd());
                orderCallBackChain.addCommand(new AddDiscountToParentOrder());
                orderCallBackChain.addCommand(new ReleaseCustomerLockCmd());
                orderCallBackChain.addCommand(new SaveOrderCmd());
                orderCallBackChain.addCommand(new DiscountTicketToUsedCmd());
                orderCallBackChain.addCommand(new AddTicketToParentOrderCmd());
                orderCallBackChain.addCommand(new GenerateSatisfyAndCutCodeCmd());
                orderCallBackChain.addCommand(new AddTicketToCustomerCmd());
                orderCallBackChain.addCommand(new DealCustomerDataInOrderCmd());
                orderCallBackChain.addCommand(new AddScoreToCustomerCmd());
                orderCallBackChain.addCommand(new DealCustomerTicketCmd());
                orderCallBackChain.addCommand(new DealPayTypeCmd());
                orderCallBackChain.addCommand(new CalPartFoodPaidNumberCmd());
                orderCallBackChain.addCommand(new CalPayEndCmd());
                orderCallBackChain.addCommand(new PayEndCmd());
                orderCallBackChain.addCommand(new AddDiscountFeeForPartPayParentOrder());
                orderCallBackChain.addCommand(new PartPayEndCmd());
                orderCallBackChain.addCommand(new UpdateStatusCmd());
                orderCallBackChain.addCommand(new sendAndPrinterOrderInB_Cmd());
                orderCallBackChain.addCommand(new SendAndPrinterOrderInC_Cmd());
                orderCallBackChain.addCommand(new PartOrderPrinterCmd());
                orderCallBackChain.addCommand(new WebSocketCmd());
                Context context = new ContextBase();
                context.put(ContextConstant.CONTEXT_ORDER, order);
                orderCallBackChain.execute(context);
            } finally {
                //做一些清除处理
                ThreadContext.remove(THREAD_LOCAL_VARIABLE_PRINTER_ORDER_ID_IN_C);
                ThreadContext.remove(THREAD_LOCAL_VARIABLE_CARD_HOLDER_NAME);
                ThreadContext.remove(THREAD_LOCAL_VARIABLE_CARD_LAST4);
            }
        }
    }

}

class VerifyStatusCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();

    private static Logger logger = LoggerFactory.getLogger(VerifyStatusCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        //如果订单已经支付成功了，则不再重复处理
        if (order.getStatus() == ORDER_STATUS_END) {
            logger.info("order status is end, shopId is {}, orderId is {}", order.getShopId(), order.getId());
            return true;
        }

        logger.info("will deal shopId {}, orderId {}", order.getShopId(), order.getId());
        //锁父订单，因为都是相关的订单同时操作
        Long shopId = order.getShopId();
        Long parentOrderId = order.getParentOrderId();
        VerifyUtil.verify(() -> parentOrderId != null);

        context.put(ContextConstant.SHOP_ID, shopId);

        Order parentOrder = orderService.findOrderCanFlagDelete(shopId, parentOrderId);
        context.put(ContextConstant.PARENT_ORDER, parentOrder);

        //该订单不能已经支付
        Order orderInDb = orderService.findOrderCanFlagDelete(order.getShopId(), order.getId());
        VerifyUtil.verify(() -> !getBoolean(orderInDb.getPayed()), ResponseStatus.SYSTEM_BUSY);
        context.put(ContextConstant.ORDER_IN_DB, orderInDb);

        return false;
    }
}

class InitCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(InitCmd.class);
    private OrderService orderService = OrderService.getInstance();
    private PayTypeService payTypeService = PayTypeService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        String terminalPay = ThreadContext.get(THREAD_LOCAL_VARIABLE_STRIPE_TERMINAL_PAY);
        boolean isTerminalPay = terminalPay != null;

        //修改成终端支付
        order.setStripeTerminal(isTerminalPay);

        String cardHolderName = ThreadContext.get(THREAD_LOCAL_VARIABLE_CARD_HOLDER_NAME);
        order.setCardHolderName(cardHolderName);

        String last4 = ThreadContext.get(THREAD_LOCAL_VARIABLE_CARD_LAST4);
        order.setLast4(last4);

        //子订单永远不可见
        order.setBusinessEndShow(false);

        //已支付
        order.setPayed(true);
        //修改成终端支付
        order.setStripeTerminal(isTerminalPay);
        Config config = Config.getInstance();

        if (config.mockStripeTerminal()) {
            order.setStripeTerminal(true);
            order.setPayType(ORDER_PAY_TYPE_STRIPE);
        }

        //添加当前订单的支付方式
        order.setPayTypeBit(orderService.addPayTypeBit(0, order.getPayType()));
        payTypeService.addPayTypeNameList(order);

        //添加支付时间
        order.setPayTime(System.currentTimeMillis());

        //又付了一次款之后，不再显示已退款
        parentOrder.setReturnCash(false);

        //添加支持时间
        if (parentOrder.getPayTime() == null) {
            parentOrder.setPayTime(System.currentTimeMillis());
        }

        //处理订单号
        orderService.addOrderNo(parentOrder);
        order.setOrderNo(parentOrder.getOrderNo());

        //标记成支持过一次的订单
        parentOrder.setPayedOnce(true);
        commonService.save(parentOrder);
        logger.info("init cmd end");

        int userSelectPayType = getInt(parentOrder.getUserSelectPayType());
        logger.info("user select pay type is {}", userSelectPayType);

        if (userSelectPayType == 0) {
            userSelectPayType = USER_SELECT_PAY_TYPE_ONE_USER;
            logger.info("reset user select pay type to {}", userSelectPayType);
        }

        context.put(ContextConstant.USER_SELECT_PAY_TYPE, userSelectPayType);
        parentOrder.setUserSelectPayType(userSelectPayType);

        return false;
    }
}

class ExpireOrderCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private PayTypeService payTypeService = PayTypeService.getInstance();

    private static Logger logger = LoggerFactory.getLogger(ExpireOrderCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        //进来先判断是否是过期订单
        //或者父订单已经被删除了
        if (!(isExpireOrder(order, parentOrder) || getBoolean(parentOrder.flagDelete))) {
            logger.info("not the expired order");
            return false;
        }

        //同步失效订单的金额
        if (getDouble(order.getCostOrigin_b()) == 0D) {
            order.setCostOrigin_b(order.getCostOrigin_c());
        }

        if (getDouble(order.getUserCost_b()) == 0D) {
            order.setUserCost_b(order.getUserCost_c());
        }


        logger.info("will set to expire order {}", order.getId());
        setToExpireOrder(order);
        //将当前订单的未支付金额设置成0
        order.setLeftCash(0d);
        order.setLeft_cash_b(0d);
        order.setLeft_cash_c(0d);

        //如果是服务员端，给提示
        if (ServletUtil.getUserId() != null) {
            //先打印小票，然后给提示

            if (order.getPrinterId() != null) {
                addCashPayToPayOrder(order);
                commonService.save(order);
                PrinterService.getInstance().printOrder(order, order.getPrinterId(), false, false);
            }
            List<String> itemNotEnough = itemNotEnough(parentOrder);
            VerifyUtil.verifyZhEn(() -> false, STOCK_NOT_ENOUGH, new String[]{itemNotEnough.get(0)}, new String[]{itemNotEnough.get(1)});
        }

        return true;
    }

    /**
     * 判断订单是否是失效订单
     * <p>
     * 如果是一个人结账，超过了总的金额，算是失效订单
     * 如果是分菜结账，超过了所有菜的总和，算是失效订单
     * 如果是分钱结账，超过了总的金额，算是失效订单
     * 如果已经是B端锁定的订单，但是这个订单却是C端付款，算是失效订单
     * 如果是有锁定量的订单，判断锁定量是否足够，如果不足够了，算是失效订单
     *
     * @param payOrder    支付订单
     * @param parentOrder 总订单
     */
    public boolean isExpireOrder(Order payOrder, Order parentOrder) throws Exception {
        if (getBoolean(payOrder.getCustomerPayOrder()) && getBoolean(parentOrder.getLockByWaiter())) {
            return true;
        }

        String sessionId = payOrder.getSessionId();
        Long userId = ServletUtil.getUserId();

        String terminalPay = ThreadContext.get(THREAD_LOCAL_VARIABLE_STRIPE_TERMINAL_PAY);
        boolean isTerminalPay = terminalPay != null;

        if (!isTerminalPay && userId == null) {
            logger.info("c end user");

            //后付款的餐桌订单
            if (sessionId != null && !getBoolean(parentOrder.getPayFirst()) && OrderService.getInstance().isFoodTableOrder(parentOrder)) {
                logger.info("must be c end order");
                //判断是否在会话里面
                List<LockOrderCashOrOrderItem> lockOrderCashOrOrderItemList = getList(parentOrder.getLockOrderCashOrOrderItemList());

                //ID要相同，并且是未支付的订单
                Optional<LockOrderCashOrOrderItem> lockOrderCashOrOrderItemOptional = lockOrderCashOrOrderItemList.stream().filter(p -> equals(p.getLockSessionId(), sessionId) && !getBoolean(p.getPayed())).findAny();

                if (!lockOrderCashOrOrderItemOptional.isPresent()) {
                    logger.info("order expired {}", payOrder.getId());
                    return true;
                }
            }
        }

        //如果优惠券已经被使用过了，那么进失效订单
        List<String> ticketCodeList = payOrder.getTicketCodeList();
        if (isNotEmpty(ticketCodeList)) {
            Optional<String> usedTicketOptional = ticketCodeList.stream().filter(code -> {
                Optional<DiscountTicket> discountTicketOptional = DiscountTicketService.getInstance().findTicket(code, payOrder.getCompanyId());

                if (discountTicketOptional.isPresent()) {
                    return getBoolean(discountTicketOptional.get().getUsed());
                }

                return false;
            }).findAny();

            if (usedTicketOptional.isPresent()) {
                String usedTicketCode = usedTicketOptional.get();
                logger.info("ticket code used {}", usedTicketCode);
                return true;
            }

            logger.info("ticket code ok");
        }

        if (userId == null && parentOrder.getUserSelectPayType() == null) {
            logger.info("user id is null and parent order not select pay type, order must be canceled");
            return true;
        }

        int userSelectPayType = getInt(parentOrder.getUserSelectPayType());

        //未设置余量的菜或套餐是否有已售罄
        boolean itemIdList = orderItemSellOut(parentOrder);
        if (itemIdList) {
            logger.info("order item sell out ");
            return true;
        }

        //计算库存
        boolean stockAvailable = false;
        //如果是分菜付款，那么用分菜付款的单去做库存校验
        if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_FOOD) {
            stockAvailable = StockService.getInstance().stockOrderCallback(payOrder);
        } else {
            stockAvailable = StockService.getInstance().stockOrderCallback(parentOrder);
        }

        if (!stockAvailable) {
            logger.error("stock not available {}", parentOrder.getId());
            return true;
        }

        logger.info("stock available");

        //只要钱没有超，都算是正常的订单
        if (parentOrder.getUserSelectPayType() == null || Arrays.asList(USER_SELECT_PAY_TYPE_ONE_USER, USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED, USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED).contains(userSelectPayType)) {
            Double left = parentOrder.getLeftCash();
            double leftAfterPay = formatDouble(getDouble(left) - getDouble(payOrder.getPayOrderCost()));
            logger.info("left after pay is {}", leftAfterPay);

            //小于0，但是大于0.1
            if (leftAfterPay < 0 && leftAfterPay > -0.1) {
                return false;
            }

            return showMoneyDouble(leftAfterPay) < 0;
        } else if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_FOOD) {
            //如果是分钱结账，如果有一个菜的已支付数量加起来超过了总数，那么也是过期订单
            List<OrderItem> orderItemList = getList(payOrder.getOrderItemDetailList());

            //先按照菜品来分组
            Map<Long, List<OrderItem>> orderItemMap = orderItemList.stream().collect(Collectors.groupingBy(p -> p.getId()));
            for (Map.Entry<Long, List<OrderItem>> entry : orderItemMap.entrySet()) {
                Long orderItemId = entry.getKey();
                List<OrderItem> orderItemListInFor = entry.getValue();
                int count = orderItemListInFor.stream().mapToInt(p -> p.getCount()).sum();
                logger.info("order item is {} count is {}", orderItemId, count);

                Optional<OrderItem> orderItemInParentOptional = orderService.filterOrderItemListOptional(parentOrder, orderItemId);
                if (orderItemInParentOptional.isPresent()) {
                    OrderItem orderItemInParent = orderItemInParentOptional.get();
                    int payNumber = getInt(orderItemInParent.getPayNumber());
                    if (payNumber + count > orderItemInParent.getCount()) {
                        logger.info("order item {} over pay", orderItemId);
                        return true;
                    } else {
                        logger.info("order item count ok {}", orderItemId);
                    }
                } else {
                    logger.info("can not find the order item in parent order");
                }
            }
        }

        logger.info("its ok for the pay order");
        return false;
    }

    /**
     * 将订单设置成过期订单
     *
     * @param order
     */
    public void setToExpireOrder(Order order) throws Exception {
        order.setExpiredOrder(true);
        order.setBusinessEndShow(true);
        order.setPayed(true);
        //不能被删除
        order.setFlagDelete(false);
        //订单失效了
        OrderOpLogService opLogService = OrderOpLogService.getInstance();
        OrderOpLog foodTableContent = opLogService.content(order, Constant.ORDER_EXPIRATION);
        opLogService.expirationOrderLog(order, foodTableContent);
        commonService.save(order);
    }

    //订单未打印的菜品是否有售罄的菜
    private boolean orderItemSellOut(Order parentOrder) throws Exception {
        List<Long> orderItemIdList = orderService.getUnPrinterOrderItemIdList(parentOrder);
        List<OrderItem> orderItems = orderService.filterOrderItemList(parentOrder, orderItemIdList);
        Order parentOrderClone = (Order) ClassUtil.clone(parentOrder);
        parentOrderClone.setOrderItemDetailList(orderItems);
        List<Long> allItemId = getAllItemId(parentOrderClone);

        for (Long item : allItemId) {
            Item itemInDb = ItemService.getInstance().findItem(item);
            if (getBoolean(itemInDb.getSaleOut())) {
                logger.info("has been item sell out {}", itemInDb.getId());
                return true;
            }
        }
        return false;
    }

    /**
     * 失效订单时，给订单添加支付方式
     *
     * @param order
     */
    private void addCashPayToPayOrder(Order order) throws Exception {
        if (order.getCashPayCallBack() != null) {
            CashPay cashPayCallBack = order.getCashPayCallBack();
            cashPayCallBack.setPayType(ORDER_PAY_TYPE_CASH);
            PayType payType = payTypeService.filterPayType(order.getShopId(), cashPayCallBack.getPayType());
            cashPayCallBack.setPayTypeName_zh(payType.getName_zh());
            cashPayCallBack.setPayTypeName_en(payType.getName_en());

            List<CashPay> orderCashPayList = new ArrayList<>();
            orderCashPayList.add(cashPayCallBack);
            order.setCashPayList(orderCashPayList);
        } else {
            CashPay cashPay = new CashPay();
            cashPay.setPayType(order.getPayType());
            PayType payType = payTypeService.filterPayType(order.getShopId(), order.getPayType());
            cashPay.setPayTypeName_zh(payType.getName_zh());
            cashPay.setPayTypeName_en(payType.getName_en());

            cashPay.setStripePayType(order.getSubStripePayType());
            cashPay.setOrderId(order.getId());
            cashPay.setPayCash(order.getUserCost());
            List<CashPay> cashPayList = new ArrayList<>();
            cashPayList.add(cashPay);
            order.setCashPayList(cashPayList);
        }
    }

    //菜已售罄，对STOCK_NOT_ENOUGH数据修改
    private List<String> itemNotEnough(Order parentOrder) throws Exception {
        List<Long> itemIdListInDb = getAllItemId(parentOrder);
        List<String> itemName_ZhList = new ArrayList<>();
        List<String> itemName_EnList = new ArrayList<>();
        //订单所有的菜id，包括套餐里面的菜
        for (Long itemId : itemIdListInDb) {
            Item itemInDb = ItemService.getInstance().findItem(itemId);
            ZhEnService.getInstance().dealZhEn(itemInDb);
            //已售罄的菜
            if (getBoolean(itemInDb.getSaleOut())) {
                itemName_ZhList.add(itemInDb.getName_zh());
                itemName_EnList.add(itemInDb.getName_en());
            }
        }
        logger.info("get all {} order sell out item name_zh {} and name_en {}", parentOrder.getId(), itemName_ZhList, itemName_EnList);
        String itemName_zh = String.join(",", itemName_ZhList);
        String itemName_en = String.join(",", itemName_EnList);
        List<String> itemNameZhAndEn = new ArrayList<>();
        itemNameZhAndEn.add(itemName_zh);
        itemNameZhAndEn.add(itemName_en);
        return itemNameZhAndEn;
    }

    //得到订单所有菜id
    private List<Long> getAllItemId(Order parentOrder) {
        List<OrderItem> orderItemDetailList = parentOrder.getOrderItemDetailList();
        List<Long> itemIdListInDb = new ArrayList<>();
        for (OrderItem orderItem : orderItemDetailList) {
            //是否是套餐
            if (getBoolean(orderItem.getSetMenu())) {
                List<SetMenuOneItem> setMenuOneItemList = orderItem.getSetMenuOneItemList();
                for (SetMenuOneItem setMenuOneItem : setMenuOneItemList) {
                    //套餐里面菜id
                    for (Item item : setMenuOneItem.getItemList()) {
                        itemIdListInDb.add(item.getId());
                    }
                }
            }
            itemIdListInDb.add(orderItem.getItemId());
        }
        //订单里面的菜id要大于0
        List<Long> itemIdList = itemIdListInDb.stream().filter((itemId) -> itemId > 0).collect(Collectors.toList());
        logger.info("get all {} order itemId {}", parentOrder, itemIdList);
        //去重
        HashSet<Long> set = new HashSet<>(itemIdList);
        return new ArrayList<>(set);
    }
}

class StartCmd implements Command {
    private static Logger logger = LoggerFactory.getLogger(StartCmd.class);
    private OrderService orderService = OrderService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        //只有实际支付成功了，才添加支付方式
        parentOrder.setPayTypeBit(orderService.addPayTypeBit(parentOrder.getPayTypeBit(), order.getPayType()));
        parentOrder.setPayTypeNameList(PayTypeService.getInstance().filterOrderPayTypeNameList(parentOrder));

        //支付过后，退款理由清除
        parentOrder.setReturnReasonList(null);
        parentOrder.setBusinessEndShow(true);

        logger.info("start cmd");
        return false;
    }
}

/**
 * 相关子订单存在，为服务员端分钱，分钱支付
 */
class SubOrderExistCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private PayTypeService payTypeService = PayTypeService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(SubOrderExistCmd.class);

    /**
     * 如果是由子订单创建的支付订单，那么子订单同时更新状态
     * <p>
     * 如果出现了，在前端退款后再操作
     *
     * @param context
     */
    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        int userSelectPayType = (Integer) context.get(ContextConstant.USER_SELECT_PAY_TYPE);

        if (order.getRelatedSubOrderId() == null) {
            logger.info("do not has related sub order id");
            return false;
        }

        logger.info("sub order exist");

        Order subOrder = orderService.findOrder(order.getShopId(), order.getRelatedSubOrderId());
        subOrder.setPayed(true);
        subOrder.setPayedWith3rd(order.getPayedWith3rd());
        subOrder.setStatus(ORDER_STATUS_END);
        subOrder.setCostOrigin(order.getUserCost() + showMoneyDouble(order.getDiscountFee()) - showMoneyDouble(order.getServiceFee()) - showMoneyDouble(order.getConsumptionTax()));

        if (isNotEmpty(order.getTicketCodeList())) {
            subOrder.getTicketCodeList().addAll(order.getTicketCodeList());
            subOrder.setDiscountFee(order.getDiscountFee());
        }

        subOrder.setServiceFee(order.getServiceFee());
        subOrder.setConsumptionTax(order.getConsumptionTax());

        subOrder.setUserCost(order.getUserCost());
        subOrder.setUserCost_b(order.getUserCost());
        subOrder.setUserCost_c(order.getUserCost());
        subOrder.setPayType(order.getPayType());

        subOrder.setCustomerId(order.getCustomerId());

        subOrder.setStripeTerminal(order.getStripeTerminal());

        //eft支付
        subOrder.setEftPay(order.getEftPay());

        //添加子订单的支付方式
        subOrder.setPayTypeBit(order.getPayTypeBit());
        payTypeService.addPayTypeNameList(subOrder);

        //添加持卡人姓名
        subOrder.setCardHolderName(order.getCardHolderName());

        //添加卡号后4位
        subOrder.setLast4(order.getLast4());

        commonService.save(subOrder);

        //子订单支付成功日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(subOrder, ORDER_PAY_OK);
        orderOpLogService.subOrderPayOkLog(subOrder, orderOpLog);

        //添加子单号
        order.setSubOrderId(subOrder.getSubOrderId());

        return false;
    }
}


/**
 * 处理子订单不存在的情况，也就是C端自定义金额结账的时候
 */
class SubOrderNoneExistCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(SubOrderNoneExistCmd.class);
    private OrderService orderService = OrderService.getInstance();
    private PayTypeService payTypeService = PayTypeService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        try {
            if (order.getRelatedSubOrderId() == null && Arrays.asList(USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED, USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON, USER_SELECT_PAY_TYPE_PART_FOOD).contains(getInt(order.getUserSelectPayType()))) {
                logger.info("will create sub order for {}", order.getId());
                List<Order> allSubOrderList = orderService.findAllSubOrders(order.getShopId(), order.getParentOrderId());
                allSubOrderList = allSubOrderList.stream().filter(p -> getBoolean(p.getPayed())).collect(Collectors.toList());

                if (isEmpty(allSubOrderList)) {
                    logger.info("will order is empty");
                    order.setSubOrderId(1);
                } else {
                    int maxSubOrderId = allSubOrderList.stream().mapToInt(p -> p.getSubOrderId()).max().getAsInt();
                    order.setSubOrderId(maxSubOrderId + 1);
                    logger.info("will add sub order id {}", order.getSubOrderId());
                }

                //创建一个子订单，以方便在B端显示
                Order subOrder = orderService.createSubOrder(parentOrder);
                subOrder.setUserCost(order.getUserCost());
                subOrder.setSubOrderId(order.getSubOrderId());
                //如果是分菜结账，那么添加订单里面的菜
                if (getInt(order.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_FOOD) {
                    subOrder.setOrderItemDetailList(new ArrayList<>());
                    subOrder.getOrderItemDetailList().addAll(order.getOrderItemDetailList());
                }

                subOrder.setStatus(ORDER_STATUS_END);
                subOrder.setPayed(true);
                subOrder.setCostOrigin(order.getUserCost() + showMoneyDouble(order.getDiscountFee()) - showMoneyDouble(order.getServiceFee()) - showMoneyDouble(order.getConsumptionTax()));
                //子订单在b端显示小计的价格
                subOrder.setCostOrigin_b(order.getUserCost() + showMoneyDouble(order.getDiscountFee()) - showMoneyDouble(order.getServiceFee()) - showMoneyDouble(order.getConsumptionTax()));
                subOrder.setDiscountFee(order.getDiscountFee());
                //子订单在b端显示的总计的价格
                subOrder.setUserCost_b(order.getUserCost());
                subOrder.setUserCost(order.getUserCost());
                //添加服务费显示
                subOrder.setServiceFee(order.getServiceFee());
                subOrder.setServiceFee_b(order.getServiceFee());
                subOrder.setServiceFee_c(order.getServiceFee());

                subOrder.setConsumptionTax(order.getConsumptionTax());
                subOrder.setPayType(order.getPayType());
                subOrder.setCustomerId(order.getCustomerId());
                logger.info("set new sub order payed with 3rd {}", false);
                //一定是C端的订单，如果支付了肯定是第3方的支付
                subOrder.setPayedWith3rd(false);

                //添加优惠券列表
                subOrder.setTicketCodeList(order.getTicketCodeList());

                subOrder.setPayTypeBit(order.getPayTypeBit());
                payTypeService.addPayTypeNameList(subOrder);

                commonService.directSave(subOrder);
                logger.info("create sub order id {}", subOrder.getId());

                //添加相关的子订单
                order.setRelatedSubOrderId(subOrder.getId());


                //如果是自定义分钱结账，那么未分配的金额需要处理
                if (order.getUserSelectPayType() == USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED) {
                    Double unassignCash = parentOrder.getUnassignCash();

                    if (unassignCash == null) {
                        unassignCash = parentOrder.getUserCost();
                    }

                    parentOrder.setUnassignCash(unassignCash - order.getPayOrderCost());
                }
            }
        } catch (Exception e) {
            logger.error("e", e);
        }

        return false;
    }
}

/**
 * 给父订单添加优惠金额
 */
class AddDiscountToParentOrder implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddDiscountToParentOrder.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        if (order.getDiscountFee() == null) {
            logger.info("add discount fee is null");
            return false;
        }

        Double parentDiscountFee = getDouble(parentOrder.getDiscountFee());
        parentOrder.setDiscountFee(parentDiscountFee + order.getRealDiscountFee());

        return false;
    }
}

/**
 * 释放掉C端的锁
 */
class ReleaseCustomerLockCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(ReleaseCustomerLockCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);
        //将C端的锁定数据改成已支付

        String sessionId = order.getSessionId();
        if (isEmpty(sessionId)) {
            logger.info("its should be b end order");
            return false;
        }

        //C端的订单，如果是找到了，那么将该锁修改成已支付
        List<LockOrderCashOrOrderItem> lockOrderCashOrOrderItemList = getList(parentOrder.getLockOrderCashOrOrderItemList());
        List<LockOrderCashOrOrderItem> sameSessionLockList = lockOrderCashOrOrderItemList.stream().filter(p -> !getBoolean(p.getPayed()) && equals(p.getLockSessionId(), sessionId)).collect(Collectors.toList());

        for (LockOrderCashOrOrderItem lockOrderCashOrOrderItem : sameSessionLockList) {
            logger.info("session id {} pay lock payed", lockOrderCashOrOrderItem.getLockSessionId());
            lockOrderCashOrOrderItem.setPayed(true);
        }

        return false;
    }
}

class SaveOrderCmd implements Command {
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        //支付订单的数据修改
        commonService.directSave(order);
        return false;
    }
}

/**
 * 将支付订单里面的优惠卷改成已支付
 */
class DiscountTicketToUsedCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(DiscountTicketToUsedCmd.class);
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        try {
            List<String> codeList = orderService.getCodeList(order);
            logger.info("code list is {}", StringUtils.join(codeList, ","));

            if (isNotEmpty(codeList)) {
                for (String ticketCode : codeList) {
                    DiscountTicket discountTicket = DiscountTicketService.getInstance().findTicketByCode(ticketCode, order.getCompanyId());

                    //店长券不做已使用处理
                    if (DiscountTicketService.getInstance().isWaiterLeaderTicket(discountTicket)) {
                        logger.info("its waiter leader ticket");
                        continue;
                    }

                    Optional<CustomerTicket> customerTicketOptional = CustomerService.getInstance().findCustomerTicket(ticketCode, order.getCustomerId());

                    if (discountTicket != null) {
                        if (customerTicketOptional.isPresent()) {
                            CustomerTicket customerTicket = customerTicketOptional.get();
                            customerTicket.setUsed(true);
                            commonService.save(customerTicket);
                        }
                        logger.info("code {} find the ticket {}", ticketCode, discountTicket.getId());
                        discountTicket.setUsed(true);
                        commonService.save(discountTicket);
                    } else {
                        logger.info("can not find the code {}", ticketCode);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("e", e);
        }

        return false;
    }
}

/**
 * 将支付订单的优惠券添加到总订单上面
 */
class AddTicketToParentOrderCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddTicketToParentOrderCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        if (isEmpty(order.getTicketCodeList())) {
            logger.info("do not use ticket");
            return false;
        }

        List<String> ticketCodeList = getList(parentOrder.getTicketCodeList());
        ticketCodeList.addAll(order.getTicketCodeList());

        //去重
        HashSet<String> ticketSet = new LinkedHashSet<>(ticketCodeList);
        parentOrder.setTicketCodeList(new ArrayList<String>(ticketSet));
        return false;
    }
}

/**
 * 产生返回的消费后金额券
 */
class GenerateSatisfyAndCutCodeCmd implements Command, Tools {
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(GenerateSatisfyAndCutCodeCmd.class);

    /**
     * 判断当前支付订单是否满足消费后金额优惠卷,如果满足了，需要把优惠卷绑定到订单上面，以方便前端做查询绑定
     *
     * @param context The {@link Context} to be processed by this
     *                {@link Command}
     * @return
     * @throws Exception
     */
    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        DiscountTicketService discountTicketService = DiscountTicketService.getInstance();
        List<DiscountTicket> discountTicketList = discountTicketService.searchAfterCostTicket(order.getCompanyId(), order.getShopId());
        Optional<DiscountTicket> discountTicketOptional = discountTicketService.filterTicket(discountTicketList, order);
        //先简单找一条返回
        if (discountTicketOptional.isPresent()) {
            DiscountTicket discountTicket = discountTicketOptional.get();
            String setSatisfyAndCutCode = DiscountTicketService.getInstance().generateSatisfyAndCutTicket(discountTicket, order);

            if (isNotEmpty(setSatisfyAndCutCode)) {
                order.setSatisfyAndCutCode(setSatisfyAndCutCode);
                order.setSatisfy(discountTicket.getAfterAtLeast());
                commonService.directSave(order);
            }
        }

        return false;
    }
}

/**
 * 将消费后金额的优惠券绑定给会员账号
 */
class AddTicketToCustomerCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddTicketToCustomerCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        try {
            if (isEmpty(order.getSatisfyAndCutCode())) {
                logger.info("do not has the satisfy and cut code");
                return false;
            }

            Long customerId = order.getCustomerId();
            if (customerId == null) {
                return false;
            }

            Long companyId = order.getCompanyId();
            String satisfyAndCutCode = order.getSatisfyAndCutCode();
            DiscountTicket discountTicket = DiscountTicketService.getInstance().findTicketByCode(satisfyAndCutCode, companyId);
            CustomerService.getInstance().addTicketToCustomer(discountTicket, customerId, order.getId(), true, null);
        } catch (Exception e) {
            //有异常不影响主流程的处理
            logger.error("e", e);
        }

        return false;
    }
}

/**
 * 处理订单里面的会员的数据
 */
class DealCustomerDataInOrderCmd implements Command, Tools {
    private CustomerService customerService = CustomerService.getInstance();

    private static Logger logger = LoggerFactory.getLogger(DealCustomerDataInOrderCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        Long customerId = order.getCustomerId();
        //没有会员的数据
        if (customerId == null) {
            logger.info("not the customer order");
            return false;
        }

        logger.info("begin");


        Customer customer = customerService.findCustomer(customerId);
        if (customer == null) {
            logger.info("can not find the customer");
            return false;
        }

        //设置年龄
        Integer age = customerService.getAge(customer);
        if (age != null) {
            if (age < 20) {
                order.setAge(10);
            } else if (age < 30) {
                order.setAge(20);
            } else if (age < 40) {
                order.setAge(30);
            } else {
                order.setAge(40);
            }
        }

        //设置性别
        order.setSex(customer.getSex());

        //设置新客，还是熟客
        List<CustomerOrder> customerOrderList = customerService.findCustomerOrder(customerId);
        //没有下过订单，算是新客人
        order.setNewCustomerOrder(isEmpty(customerOrderList) ? 0 : 1);

        //添加会员等级
        if (customer.getVipLevelId() != null) {
            order.setCustomerLevel(((Number) (customer.getVipLevelId())).intValue());
        }

        //是否是VIP会员消费
        if (getBoolean(customer.getVip()) && getLong(customer.getVipEndTime()) > System.currentTimeMillis()) {
            order.setCustomerVip(true);
        }

        return false;
    }
}

class DealCustomerTicketCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(DealCustomerTicketCmd.class);
    private CustomerService customerService = CustomerService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        if (order.getCustomerId() == null) {
            logger.info("customer Id is empty");
            return false;
        }

        List<String> codeList = null;

        OrderService orderService = OrderService.getInstance();

        codeList = orderService.getOrderTicketCodeList(order);

        if (isEmpty(codeList)) {
            logger.info("code list is empty~");
            return false;
        }

        Long customerId = order.getCustomerId();
        Customer customer = CustomerService.getInstance().findCustomer(customerId);
        Long companyId = order.getCompanyId();
        DiscountTicketService discountTicketService = DiscountTicketService.getInstance();

        for (String code : codeList) {
            Optional<com.xishu.entity.customer.CustomerTicket> customerTicketOptional = customerService.findCustomerTicket(customerId, code);
            if (!customerTicketOptional.isPresent()) {
                logger.info("{} is not exist", code);
                continue;
            }

            com.xishu.entity.customer.CustomerTicket customerTicket = customerTicketOptional.get();

            //将会员的优惠券设置成已使用
            customerTicket.setUsed(true);
            commonService.save(customerTicket);
            logger.info("set customer ticket to used {}", customerTicket.getId());

            if (getBoolean(customerTicket.getLimitNumber())) {
                logger.info("limit the number");
                continue;
            }

            VipLevelTicketConfig vipLevelTicketConfig = null;

            //VIP券，添加特殊的判断条件
            if (getBoolean(customerTicket.getVip())) {
                //VIP送的券，判断是否还是VIP，以及VIP是否过期了
                if (!customer.getVip()) {
                    logger.info("user is not vip");
                    continue;
                }

                //vip已经过期了
                if (getLong(customer.getVipEndTime()) < System.currentTimeMillis()) {
                    logger.info("customer vip end time is {}, date {}", customer.getVipEndTime(), DateUtil.formatDate(getLong(customer.getVipEndTime())));
                    continue;
                }

                //添加VIP的等级
                Optional<VipLevelTicketConfig> vipLevelTicketConfigOptional = customerService.findVipTicketConfigById(customerTicket.getLevelTicketId());
                if (!vipLevelTicketConfigOptional.isPresent()) {
                    logger.info("the config is delete");
                    continue;
                }

                vipLevelTicketConfig = vipLevelTicketConfigOptional.get();
            } else {
                Long levelTicketId = customerTicket.getLevelTicketId();
                if (levelTicketId != null) {
                    Optional<VipLevelTicketConfig> vipLevelTicketConfigOptional = customerService.findVipTicketConfigById(levelTicketId);
                    if (!vipLevelTicketConfigOptional.isPresent()) {
                        logger.info("the level ticket config deleted");
                        continue;
                    }

                    vipLevelTicketConfig = vipLevelTicketConfigOptional.get();

                    //当前会员的等级已经变了，也不再送了
                    if (!equals(customer.getVipLevelId(), vipLevelTicketConfig.getLevelId())) {
                        logger.info("the level changed customer now level {}, ticket config level is {}", customer.getVipLevelId(), vipLevelTicketConfig.getLevelId());
                        continue;
                    }
                }
            }

            DiscountTicket discountTicket = discountTicketService.findTicketByCode(code, companyId);
            Long parentTicketId = discountTicket.getParentId();
            if (parentTicketId == null) {
                logger.info("can not find the parent ticket {}", discountTicket.getId());
                continue;
            }

            DiscountTicket parentTicket = discountTicketService.findTicket(discountTicket.getId());

            //已经过期了，那么不再派券
            if (System.currentTimeMillis() > parentTicket.getEndTime()) {
                logger.info("ticket expired {}", parentTicket.getId());
                continue;
            }

            DiscountTicket realTicket = null;
            if (getBoolean(customerTicket.getAssignByExcel())) {
                realTicket = discountTicketService.createRealTicketForAssignByExcel(parentTicket, customerTicket);
            } else {
                //创建新的优惠券给用户
                realTicket = discountTicketService.createRealTicket(parentTicket);
            }
            customerService.addTicketToCustomer(realTicket, customerId, order.id, false, vipLevelTicketConfig);
        }

        return false;
    }

}

/**
 * 给会员添加积分
 */
class AddScoreToCustomerCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddScoreToCustomerCmd.class);
    private CustomerService customerService = CustomerService.getInstance();
    private CompanyService companyService = CompanyService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        try {
            if (order.getCustomerId() == null) {
                logger.info("order not bind the customer");
                return false;
            }

            logger.info("begin");

            Long customerId = order.getCustomerId();

            Optional<Customer> customerOptional = customerService.findCustomerOptional(customerId);

            if (!customerOptional.isPresent()) {
                logger.info("can not find the customer {}", customerId);
                return false;
            }

            Customer customer = customerOptional.get();
            //查询餐厅的积分配置
            Optional<VipLevel> vipLevelOptional = customerService.findCustomerVipLevel(customer);

            //通常是会员等级被删除了找不到
            if (!vipLevelOptional.isPresent()) {
                //添加消费历史记录
                customerService.addCustomerOrderHistory(order, 0);
                logger.info("can not find the vip level for {}", customer.getVipLevelId());
                return false;
            }

            VipLevel vipLevel = vipLevelOptional.get();
            logger.info("find the vip level is {}", vipLevel.getId());

            //再看会员功能是否开启
            Long companyId = vipLevel.getCompanyId();
            Company company = companyService.findCompanyById(companyId);

            if (!getBoolean(company.getCompanyConfig().getCustomerOpen())) {
                logger.info("customer open is close for {}", companyId);
                return false;
            }

            //按照消费的金额来算积分
            if (getInt(vipLevel.getConsumeByCost()) == VIP_LEVEL_SCORE_EXCHANGE_TYPE_CASH) {
                double userCost = getDouble(order.getUserCost());

                if (userCost < 0) {
                    logger.info("order user cost less than 0");
                    //添加消费历史记录
                    customerService.addCustomerOrderHistory(order, 0);
                    return false;
                }

                if (getInt(vipLevel.getUserCost()) <= 0) {
                    //添加消费历史记录
                    customerService.addCustomerOrderHistory(order, 0);
                    logger.info("vip level user cost less than 0");
                    return false;
                }

                //几倍的值
                int userCostTime = (int) (userCost / vipLevel.getUserCost());
                //user cost score 允许为0，那么就是简直的加消费历史记录
                int score = userCostTime * getInt(vipLevel.getUserCostScore());
                int customerScore = getInt(customer.getScore());
                //添加历史记录
                customer.setScoreHistory(getInt(customer.getScoreHistory()) + score);
                //加上这一次的积分
                customer.setScore(customerScore + score);
                commonService.save(customer);

                logger.info("add score history");
                //添加积分历史记录
                customerService.addScoreHistory(customerId, SCORE_HISTORY_TYPE_ADD, score, order.getId(), order.getShopId());

                //添加消费历史记录
                customerService.addCustomerOrderHistory(order, score);
            } else {
                //按照消费的次数来算积分
                int orderNumber = getInt(customer.getOrderNumber());
                int costTime = getInt(vipLevel.getCostTime());
                if (costTime < 0) {
                    //添加消费历史记录
                    customerService.addCustomerOrderHistory(order, 0);
                    logger.info("can not deal cost time less than 0");
                    return false;
                }

                //以前消费的历史记录加上现在的次数，超过了定义的次数，那么可以增加积分
                if ((orderNumber + 1) >= costTime) {
                    int customerScore = getInt(customer.getScore());
                    int costTimeScore = getInt(vipLevel.getCostTimeScore());

                    //定义的积分小于等于0，不做处理
                    if (costTimeScore <= 0) {
                        //添加消费历史记录
                        customerService.addCustomerOrderHistory(order, 0);
                        logger.info("cost time score less equals than 0");
                        return false;
                    }

                    logger.info("add score history");

                    customer.setScoreHistory(getInt(customer.getScoreHistory()) + costTimeScore);
                    customer.setScore(customerScore + costTimeScore);
                    commonService.save(customer);

                    //添加积分历史记录
                    customerService.addScoreHistory(customerId, SCORE_HISTORY_TYPE_ADD, costTimeScore, order.getId(), order.getShopId());

                    //添加消费历史记录
                    customerService.addCustomerOrderHistory(order, costTimeScore);
                }

            }

            //处理会员的等级
            customerService.setCustomerVipLevel(customer);
            //处理会员等级相关的优惠券
            customerService.addLevelTicketToCustomer(customer);
        } catch (Exception e) {
            //会员系统处理异常不影响正常流程
            logger.error("e", e);
        }

        return false;
    }
}

/**
 * 处理支付方式
 */
class DealPayTypeCmd implements Command, Tools {
    private PayTypeService payTypeService = PayTypeService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(DealPayTypeCmd.class);


    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        //如果有现金支付，那么把现金支付的收款，找零保存
        if (order.getCashPayCallBack() != null) {
            logger.info("will deal getCashPayCallBack");
            CashPay cashPayCallBack = order.getCashPayCallBack();
            PayType payType = payTypeService.filterPayType(order.getShopId(), cashPayCallBack.getPayType());
            cashPayCallBack.setPayType(ORDER_PAY_TYPE_CASH);
            cashPayCallBack.setPayTypeName_zh(payType.getName_zh());
            cashPayCallBack.setPayTypeName_en(payType.getName_en());
            List<CashPay> cashPayList = getList(parentOrder.getCashPayList());
            //如果存在相同的ID,那么覆盖掉
            cashPayList = cashPayList.stream().filter(p -> !equals(p.getOrderId(), cashPayCallBack.getOrderId())).collect(Collectors.toList());
            //重新添加
            cashPayList.add(cashPayCallBack);
            parentOrder.setCashPayList(cashPayList);
            commonService.save(parentOrder);

            //订单本身也添加，以方便后续去打印`
            List<CashPay> orderCashPayList = new ArrayList<>();
            orderCashPayList.add(cashPayCallBack);
            order.setCashPayList(orderCashPayList);
        } else {
            logger.info("will deal other pay method");
            //添加支付方式
            CashPay cashPay = new CashPay();
            cashPay.setPayType(order.getPayType());
            PayType payType = payTypeService.filterPayType(order.getShopId(), order.getPayType());
            cashPay.setPayTypeName_zh(payType.getName_zh());
            cashPay.setPayTypeName_en(payType.getName_en());
            cashPay.setStripePayType(order.getSubStripePayType());
            cashPay.setOrderId(order.getId());
            cashPay.setPayCash(order.getUserCost());
            List<CashPay> cashPayList = getList(parentOrder.getCashPayList());
            //如果存在相同的ID,那么覆盖掉
            cashPayList = cashPayList.stream().filter(p -> !equals(p.getOrderId(), cashPay.getOrderId())).collect(Collectors.toList());
            //重新添加
            cashPayList.add(cashPay);
            parentOrder.setCashPayList(cashPayList);
            logger.info("cashPay is {}", cashPay);
            commonService.save(parentOrder);

            //订单本身也添加，以方便后续去打印
            List<CashPay> orderCashPayList = new ArrayList<>();
            orderCashPayList.add(cashPay);
            order.setCashPayList(orderCashPayList);
        }

        return false;
    }
}

/**
 * 如果是分菜结账，那么打印分菜结账信息
 * 这里同时将菜的已支付数量加1
 */
class CalPartFoodPaidNumberCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(CalPartFoodPaidNumberCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);
        int userSelectPayType = (Integer) context.get(ContextConstant.USER_SELECT_PAY_TYPE);

        if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_FOOD) {
            logger.info("will update item number");
            //如果是分菜付款，则更新已付款的数量
            List<OrderItem> orderItemDetailList = getList(order.getOrderItemDetailList());

            for (OrderItem orderItem : orderItemDetailList) {
                Optional<OrderItem> itemOptional = getList(parentOrder.getOrderItemDetailList()).stream().filter(p -> p.getId().longValue() == orderItem.getId().longValue()).findFirst();

                //已付款数量加1
                if (itemOptional.isPresent()) {
                    OrderItem findOrderItem = itemOptional.get();
                    logger.info("will set item {} payed", findOrderItem.getId());
                    findOrderItem.setPayNumber(getInt(findOrderItem.getPayNumber()) + 1);
                } else {
                    logger.error("can not find the order item {}", orderItem.getId());
                }
            }
        }

        return false;
    }
}

/**
 * 计算订单是否已经支付完成了
 */
class CalPayEndCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(CalPayEndCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);
        int userSelectPayType = (Integer) context.get(ContextConstant.USER_SELECT_PAY_TYPE);

        Double left = parentOrder.getLeftCash();
        logger.info("left is {}, pay  order cost {}", left, order.getPayOrderCost());
        double leftAfterPay = formatDouble(getDouble(left) - getDouble(order.getPayOrderCost()));
        logger.info("left after pay is {}", leftAfterPay);

        boolean payEnd = isOrderPayEnd(parentOrder, userSelectPayType, leftAfterPay);
        logger.info("pay end {}", payEnd);
        context.put(ContextConstant.PAY_END, payEnd);
        context.put(ContextConstant.LEFT_AFTER_PAY, leftAfterPay);
        return false;
    }

    /**
     * 是否已经付完款了
     *
     * @param parentOrder
     * @param userSelectPayType
     * @param leftAfterPay
     * @return
     */
    private boolean isOrderPayEnd(Order parentOrder, Integer userSelectPayType, double leftAfterPay) {
        boolean payEnd = false;

        if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_FOOD) {
            //如果是分菜付款，那么全部款都付完
            //如果是B端付款，那么需要全部审核的了
            //如果是C端付款，只需要是已经确认的就可以了
            payEnd = orderService.getOrderItemList(parentOrder).stream().allMatch(orderItem -> {
                return equals(orderItem.getCount(), orderItem.getPayNumber());
            });

            logger.info("payEnd {}", payEnd);
        } else {
            payEnd = leftAfterPay <= 0;
            logger.info("all pay end {}", payEnd);
        }

        logger.info("parent order is {}", parentOrder);
        return payEnd;
    }
}

/**
 * 支付完成
 */
class PayEndCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private Config config = Config.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private FoodTableService foodTableService = FoodTableService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(PayEndCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        boolean payEnd = (Boolean) context.get(ContextConstant.PAY_END);
        if (!payEnd) {
            logger.info("not the pay end order");
            return false;
        }

        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);
        int userSelectPayType = (Integer) context.get(ContextConstant.USER_SELECT_PAY_TYPE);


        logger.info("all payed for order", parentOrder);
        parentOrder.setPayed(true);
        parentOrder.setOncePayEnd(true);
        //支付完成，一定会发送厨房或者曾经发送过厨房
        parentOrder.setSendKitchen(true);

        parentOrder.setStatus(Constant.ORDER_STATUS_END);

        //以第一次支付完成的时间为准
        if (parentOrder.getPayEndTime() == null) {
            parentOrder.setPayEndTime(System.currentTimeMillis());
        }

        //如果是分菜结账，修正父订单的服务费问题
        if (getInt(userSelectPayType) == USER_SELECT_PAY_TYPE_PART_FOOD) {
            List<Order> subOrderList = orderService.findOrderByParentId(order.getParentOrderId(), order.getShopId()).stream().filter(o -> {
                return getBoolean(o.getPayed()) && !getBoolean(o.getReturnCash()) && !getBoolean(o.getSubOrder());
            }).collect(Collectors.toList());

            logger.info("sub order list size is {}", subOrderList.size());
            double serviceFee = subOrderList.stream().mapToDouble(o -> getDouble(o.getServiceFee())).sum();
            parentOrder.setServiceFee(serviceFee);
            //修正总计,不需要处理外卖折扣，因为分菜结账一定是餐桌订单
            if (ServletUtil.isBEndPayOrder()) {
                parentOrder.setUserCost(parentOrder.getCostOrigin_b() + serviceFee + getDouble(parentOrder.getConsumptionTax()));
            } else {
                parentOrder.setUserCost(parentOrder.getCostOrigin_c() + serviceFee + getDouble(parentOrder.getConsumptionTax()));
            }
        } else if (getInt(userSelectPayType) == USER_SELECT_PAY_TYPE_ONE_USER) {
            //一个人结账的时候，修复订单数据
            parentOrder.setUserCost(order.getUserCost());
            parentOrder.setUserCost_b(order.getUserCost());
            parentOrder.setUserCost_c(order.getUserCost());

            //一个人结账，支付完成后，同步父订单的数据，与支付订单的数据
            if (ServletUtil.isBEndPayOrder()) {
                parentOrder.setCostOrigin(order.getCostOrigin_b());
                parentOrder.setCostOrigin_b(parentOrder.getCostOrigin());
                parentOrder.setCostOrigin_c(parentOrder.getCostOrigin());
            } else {
                parentOrder.setCostOrigin(order.getCostOrigin_c());
                parentOrder.setCostOrigin_b(parentOrder.getCostOrigin());
                parentOrder.setCostOrigin_c(parentOrder.getCostOrigin());
            }


            parentOrder.setPackageFee(getDouble(order.getPackageFee()));

            parentOrder.setTakeOutDiscountFee(getDouble(order.getTakeOutDiscountFee()));
            parentOrder.setTakeOutDiscountFee_b(getDouble(order.getTakeOutDiscountFee()));
            parentOrder.setTakeOutDiscountFee_c(getDouble(order.getTakeOutDiscountFee()));

            //添加优惠金额到父订单
            parentOrder.setDiscountFee(order.getDiscountFee());

            //添加eft支付到父订单
            if (isNotEmpty(ServletUtil.getUserId())) {
                parentOrder.setEftPay(order.getEftPay());
            }

            //添加小费
            parentOrder.setTipsFee(order.getTipsFee());
            if (order.getStripeTerminal() || config.mockStripeTerminal()) {
                parentOrder.setStripeTerminal(true);
            }

            //如果是第3方支付的，修改标记
            parentOrder.setPayedWith3rd(order.getPayedWith3rd());
            //一个人结账的时候，尽可能写持卡人姓名
            parentOrder.setCardHolderName(order.getCardHolderName());
            //写卡号后4位
            parentOrder.setLast4(order.getLast4());
        }

        //支付完成后，父订单将不可见
        parentOrder.setBusinessEndShow(true);
        orderService.dealTakeOutOrder(parentOrder);
        order.setTakeOutNumber(parentOrder.getTakeOutNumber());

        if (getInt(userSelectPayType) == USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON) {
            logger.info("will add payed number for id {}", order.getId());
            PartCash partCash = parentOrder.getPartCash();
            partCash.setPayedNumber(getInt(partCash.getPayedNumber()) + 1);
        }

        //如果是后付款模式，再查询下落单审核，如果存在落单审核，那么没有处理过的审核，需要都通过
        confirmOrder(parentOrder);

        //B端结账，只保留B端的菜
        if (!orderService.customerEndPay()) {
            List<OrderItem> orderItemList = order.getOrderItemDetailList().stream().filter(p -> getBoolean(p.getCheckout()) && getBoolean(p.getConfirmItem())).collect(Collectors.toList());
            order.setOrderItemDetailList(orderItemList);
            commonService.save(order);
        }

        //金额清零
        parentOrder.setLeftCash(0d);
        parentOrder.setLeft_cash_b(0d);
        parentOrder.setLeft_cash_c(0d);

        commonService.directSave(parentOrder);
        //订单支付成功日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(parentOrder, ORDER_PAY_OK);
        orderOpLogService.orderPayOkLog(parentOrder, orderOpLog);

        //餐桌订单，处理餐桌状态
        callbackDealFoodTable(order);

        return false;
    }

    /**
     * 确认落单处理
     *
     * @param parentOrder
     */
    private void confirmOrder(Order parentOrder) {
        if (!getBoolean(parentOrder.getPayFirst())) {

            List<SendToKitchenAudit> sendToKitchenAuditList = orderService.findSendToKitchenAudit(parentOrder.getShopId(), parentOrder.getId());
            sendToKitchenAuditList = sendToKitchenAuditList.stream().filter(p -> p.getStatus() == 0).collect(Collectors.toList());

            //如果是C端，将没有审核的通过
            if (orderService.customerEndPay()) {
                sendToKitchenAuditList.forEach(p -> {
                    try {
                        confirm(p);
                        p.setStatus(1);
                        commonService.save(p);
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                });
            } else {
                //如果是B端，将没有审核掉的拒绝掉
                sendToKitchenAuditList.forEach(p -> {
                    SendToKitchenAudit sendToKitchenAudit = new SendToKitchenAudit();
                    sendToKitchenAudit.setId(p.getId());
                    sendToKitchenAudit.setStatus(2);
                    try {
                        orderService.rejectAudit(parentOrder, sendToKitchenAudit);
                        commonService.save(parentOrder);

                        p.setStatus(2);
                        commonService.save(p);
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                });

                //B端结账，只保留B端的菜
                if (!orderService.customerEndPay()) {
                    List<OrderItem> orderItemList = parentOrder.getOrderItemDetailList().stream().filter(p -> getBoolean(p.getCheckout()) && getBoolean(p.getConfirmItem())).collect(Collectors.toList());
                    parentOrder.setOrderItemDetailList(orderItemList);
                    try {
                        commonService.save(parentOrder);
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                }
            }

            //做socket通知
            int confirmOrder = orderService.countConfirmOrder(parentOrder.getShopId());
            WebSocketService.getInstance().sendKitchenOrderAudit(parentOrder.getShopId(), confirmOrder);

        }
    }

    /**
     * 确认落单
     *
     * @param
     */
    private void confirm(SendToKitchenAudit sendToKitchenAudit) throws Exception {

        logger.info("confirm the order {}", sendToKitchenAudit.getOrderId());
        Order order = orderService.sendToKitchenAuditToOrder(sendToKitchenAudit);
        Order orderInDb = orderService.findOrder(order.getShopId(), order.getId());

        List<OrderItem> orderItemDetailListInDb = getList(orderInDb.getOrderItemDetailList());
        List<Long> orderItemIdList = orderService.toOrderItemIdList(order.getOrderItemDetailList());

        List<Long> sendKitchenList = orderService.getSendKitchenList(orderInDb);

        //已经落单的，不再重复落单
        orderItemIdList = orderItemIdList.stream().filter(orderItemId -> !sendKitchenList.contains(orderItemId.longValue())).collect(Collectors.toList());

        if (isEmpty(orderItemIdList)) {
            logger.info("the item id list is confirmed before");
            return;
        }

        //场景没有切换，余量也是够的,还没有设置过落单时间，那么添加落单时间
        if (orderInDb.getConfirmTime() == null) {
            orderInDb.setConfirmTime(System.currentTimeMillis());
            logger.info("add the confirm time is {}", orderInDb.getConfirmTime());
        }

        //将这些菜修改成已确认
        List<OrderItem> orderItemList = orderService.filterOrderItemList(orderInDb, orderService.toOrderItemIdList(order.getOrderItemDetailList()));
        logger.info("order item list size is {}", orderItemList.size());
        orderItemList.forEach(p -> p.setConfirmItem(true));

        commonService.save(orderInDb);

        //将C端的菜品修改成正在结算
        for (OrderItem orderItem : getList(order.getOrderItemDetailList())) {
            Optional<OrderItem> orderItemOptional = orderItemDetailListInDb.stream().filter(p -> equals(p.getId(), orderItem.getId())).findAny();
            if (orderItemOptional.isPresent()) {
                OrderItem orderItemInDb = orderItemOptional.get();
                logger.info("will set order item {} to checkout", orderItemInDb.getId());
                orderItemOptional.get().setCheckout(true);
                orderItemOptional.get().setConfirmItem(true);
            } else {
                logger.info("can not find the order item {}", orderItem.getId());
            }
        }

        //在B端可见
        orderInDb.setBusinessEndShow(true);
        //订单添加标记，说明已经发送到厨房了
        orderInDb.setSendKitchen(true);

        commonService.save(orderInDb);
    }


    private void callbackDealFoodTable(Order order) throws Exception {
        if (orderService.isFoodTableOrder(order)) {
            logger.info("will deal food table");
            List<FoodTable> foodTableList = foodTableService.findFoodTableListByOrder(order);
            if (isEmpty(foodTableList)) {
                logger.info("can not find the food table");
            } else {
                for (FoodTable table : foodTableList) {
                    //本桌已经吃完了，真删除
                    commonService.delete(table, false, null);
                    foodTableService.dealTemplateFoodTable(table);
                }
            }
        }
    }
}

class PartPayEndCmd implements Command, Tools {
    private CommonService commonService = CommonService.getInstance();
    private OrderService orderService = OrderService.getInstance();
    private FoodTableService foodTableService = FoodTableService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(PartPayEndCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        boolean payEnd = (Boolean) context.get(ContextConstant.PAY_END);
        if (payEnd) {
            logger.info("pay end order");
            return false;
        }

        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        double leftAfterPay = (double) context.get(ContextConstant.LEFT_AFTER_PAY);
        int userSelectPayType = (int) context.get(ContextConstant.USER_SELECT_PAY_TYPE);

        //只修改剩余的钱
        parentOrder.setLeftCash(leftAfterPay);
        parentOrder.setLeft_cash_b(leftAfterPay);
        parentOrder.setLeft_cash_c(leftAfterPay);

        //设置成部分付款
        if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON) {
            parentOrder.setStatus(ORDER_STATUS_PART_CASH_PAYED);
        } else if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED) {
            parentOrder.setStatus(ORDER_STATUS_PART_CASH_CUSTOMIZED_PAYED);
        } else if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_FOOD) {
            parentOrder.setStatus(ORDER_STATUS_PART_FOOD_PAYED);
        }

        logger.info("now parent order status is {}", parentOrder.getStatus());

        //如果是分钱结账，添加已支付的人数
        if (userSelectPayType == USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON) {
            logger.info("will add payed number for id {}", order.getId());
            PartCash partCash = parentOrder.getPartCash();
            partCash.setPayedNumber(getInt(partCash.getPayedNumber()) + 1);
        }

        logger.info("parent order is {}", parentOrder);
        //修改总的订单状态
        commonService.directSave(parentOrder);
        //单个支付订单成功时，处理餐桌状态
        subOrderOkDealFoodTable(order);

        return false;
    }

    private void subOrderOkDealFoodTable(Order order) throws Exception {
        if (orderService.isFoodTableOrder(order)) {
            logger.info("part pay deal");
            List<FoodTable> foodTableList = foodTableService.findFoodTableListByOrder(order);
            if (isEmpty(foodTableList)) {
                //如果餐桌都找不到，那么不用处理了
                logger.info("do not have food table list");
            } else {
                logger.info("will deal food table status");
                for (FoodTable table : foodTableList) {
                    logger.info("will modify food table {} status to part cash", table);
                    table.setStatus(Constant.FOOD_TABLE_STATUS_PART_CASH);
                    commonService.save(table);

                    foodTableService.dealTemplateFoodTable(table);
                }
            }
        }
    }
}

/**
 * 分菜分钱结完账时，处理
 */
class AddDiscountFeeForPartPayParentOrder implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddDiscountFeeForPartPayParentOrder.class);
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        boolean payEnd = (boolean) context.get(ContextConstant.PAY_END);
        int userSelectPayType = (Integer) context.get(ContextConstant.USER_SELECT_PAY_TYPE);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        if (!payEnd) {
            logger.info("not pay end");
            return false;
        }

        if (userSelectPayType == USER_SELECT_PAY_TYPE_ONE_USER) {
            logger.info("one user pay");
            return false;
        }

        List<Order> payedOrderList = orderService.findPayedOrder(parentOrder.getShopId(), parentOrder.getId());
        double discountFee = payedOrderList.stream().mapToDouble(p -> getDouble(p.getDiscountFee())).sum();

        if (discountFee == 0d) {
            logger.info("all order do not has discount fee");
            return false;
        }

        parentOrder.setDiscountFee(discountFee);
        //如果是C端请求
        if (ServletUtil.isBEndPayOrder()) {
            parentOrder.setUserCost_b(parentOrder.getUserCost() - discountFee);
        } else {
            parentOrder.setUserCost_b(parentOrder.getUserCost() - discountFee);
            //C端显示
            parentOrder.setUserCost_c(parentOrder.getUserCost_b());
        }

        parentOrder.setUserCost(parentOrder.getUserCost_b());
        commonService.save(parentOrder);

        return false;
    }
}

class UpdateStatusCmd implements Command {
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(UpdateStatusCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        logger.info("update status");
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        //最后更新子订单的状态，意味着客户端查询到子订单的状态已经更新的时候，父订单的状态也同时更新了
        order.setStatus(Constant.ORDER_STATUS_END);
        commonService.save(order);
        return false;
    }
}

class sendAndPrinterOrderInB_Cmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(sendAndPrinterOrderInB_Cmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        boolean payEnd = (boolean) context.get(ContextConstant.PAY_END);
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        if (!payEnd) {
            logger.info("not pay end request");
            return false;
        }

        if (!ServletUtil.isBEndPayOrder()) {
            logger.info("not b end request");
            return false;
        }

        sendAndPrinterOrderInB(order, parentOrder);
        //如果是在B端输入会员账号结账，存在customerId,则写入parentorder
        if (order.getCustomerId() != null) {
            parentOrder.setCustomerId(order.getCustomerId());
        }
        commonService.save(parentOrder);

        return false;
    }

    /**
     * 处理B端发送厨房，以及打印小票
     *
     * @param order
     * @param parentOrder
     * @throws Exception
     */
    private void sendAndPrinterOrderInB(Order order, Order parentOrder) throws Exception {
        //如果还有未发往厨房的菜，先过滤出来，然后发往厨房
        List<Long> unPrinterOrderItemIdList = orderService.getUnPrinterOrderItemIdList(parentOrder);
        logger.info("un printer order item list is {}", StringUtils.join(unPrinterOrderItemIdList, ","));
        //全部都没有发送的时候，需要发送
        //更新打印列表
        List<OrderItem> orderItemList = orderService.filterOrderItemList(parentOrder, true);

        if (isNotEmpty(unPrinterOrderItemIdList) && (unPrinterOrderItemIdList.size() == getList(orderItemList).size())) {
            logger.info("will send to kitchen");
            DayReportService.getInstance().addSendKitchenNumber(parentOrder, unPrinterOrderItemIdList);
            PrinterService.getInstance().printerOrderItemList(parentOrder, unPrinterOrderItemIdList);
            for (Long orderItemId : unPrinterOrderItemIdList) {
                SendPrinter sendPrinter = new SendPrinter();
                sendPrinter.setOrderItemId(orderItemId);
                sendPrinter.setCount(1l);

                if (isEmpty(parentOrder.getSendPrinterList())) {
                    parentOrder.setSendPrinterList(new ArrayList<>());
                }

                parentOrder.getSendPrinterList().add(sendPrinter);
            }

            //更新订单数据
            commonService.save(parentOrder);


            //打印厨房订单日志
            OrderOpLogService kitchenOrderOpLogService = OrderOpLogService.getInstance();
            OrderOpLog kitchenOrderOpLog = kitchenOrderOpLogService.content(parentOrder, PRINT_KITCHEN_ORDER);
            kitchenOrderOpLogService.printKitchenOrder(parentOrder, kitchenOrderOpLog);
        }

        Integer userSelectPayType = getInt(parentOrder.getUserSelectPayType());
        //如果发送了部分，但是又不是分菜结账，那么在结算完成之后打印小票
        //仅限制B端
        if (Arrays.asList(USER_SELECT_PAY_TYPE_PART_FOOD, USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON, USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED).contains(userSelectPayType)) {
            logger.info("user select part food or part cash and not one user");
            return;
        }

        //printId只有B端才能够带过来,要么一个人支付，分钱支付，分菜支付（一个人）
        Long printerId = order.getPrinterId();

        if (printerId != null) {
            parentOrder.setSatisfyAndCutCode(order.getSatisfyAndCutCode());

            if (getBoolean(order.getStripeTerminal())) {
                ThreadContext.put(THREAD_LOCAL_VARIABLE_PRINT_MERCHANT_COPY, "true");
                try {
                    PrinterService.getInstance().printOrder(parentOrder, printerId, false, false);
                } finally {
                    ThreadContext.remove(THREAD_LOCAL_VARIABLE_PRINT_MERCHANT_COPY);
                }
            }

            PrinterService.getInstance().printOrder(parentOrder, printerId, false, false);


            //如果要打印两次
            Long shopId = parentOrder.getShopId();
            Shop shop = ShopService.getInstance().findShopById(shopId);
            if (shop.getShopConfig().getPrintOrderTwiceAfterPay()) {
                PrinterService.getInstance().printOrder(parentOrder, printerId, false, false);
            }
            //打印订单，写入日志
            OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
            OrderOpLog orderOpLog = orderOpLogService.content(order, PRINT_ORDER);
            orderOpLogService.printOrderLog(order, orderOpLog);


        } else {
            logger.info("do not pass the printer id");
        }
    }
}

class SendAndPrinterOrderInC_Cmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(SendAndPrinterOrderInC_Cmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        boolean payEnd = (boolean) context.get(ContextConstant.PAY_END);
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);

        if (!payEnd) {
            logger.info("not pay end");
            return false;
        }

        //打印放在最后面
        //发送厨房并打印小票，只有一个人结账才打印小票
        sendAndPrinterOrderInC(order, parentOrder);
        printerOrderAfterPayInC(order, parentOrder);

        //C端已经支付的订单，如果有customerId，写入到parentOrder
        if (order.getCustomerId() != null) {
            parentOrder.setCustomerId(order.getCustomerId());
        }
        //订单已经发送到厨房
        commonService.save(parentOrder);

        return false;
    }

    /**
     * C端落单后打印小票
     *
     * @param order
     * @throws Exception
     */
    private void printerOrderAfterPayInC(Order order, Order parentOrder) throws Exception {
        //如果存在打印机，配置了下单成功后打印，那么打印
        if (!orderService.customerEndPay()) {
            logger.info("not customer end");
            return;
        }

        //该场景下面如果开启了付款打印才打印
        Scene scene = SceneService.getInstance().findScene(order.getSceneId());

        if (!getBoolean(scene.getPrintOrderWhenPayedOrderInC())) {
            logger.info("not config print order when pay order in c");
            return;
        }

        Optional<Printer> printerWithPayedOrder = PrinterService.getInstance().findPrinterWithPayedOrder(order.getShopId());
        if (!printerWithPayedOrder.isPresent()) {
            logger.info("not find the printer in c when pay order ");
            return;
        }

        Printer printer = printerWithPayedOrder.get();
        //如果这个打印以前打印过小票，则不再打印
        String printIdBefore = ThreadContext.get(THREAD_LOCAL_VARIABLE_PRINTER_ORDER_ID_IN_C);
        if (printIdBefore != null && Long.valueOf(printIdBefore).longValue() == printer.getId().longValue()) {
            logger.info("print {} print order before", printIdBefore);
            return;
        }

        //以前也没有打印，这次要新增打印
        PrinterService.getInstance().printOrder(parentOrder, printer.getId(), false, false);

        //打印订单，写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(parentOrder, PRINT_ORDER);
        orderOpLogService.printOrderLog(parentOrder, orderOpLog);
    }


    /**
     * 判断是否是B端支付的订单
     */
    public boolean isBEndPayOrder() {
        Long userId = ServletUtil.getUserId();
        if (userId != null) {
            return true;
        }

        String terminalPay = ThreadContext.get(THREAD_LOCAL_VARIABLE_STRIPE_TERMINAL_PAY);
        return terminalPay != null;
    }


    /**
     * 处理C端发送厨房，以及打印小票
     *
     * @param order
     * @param parentOrder
     * @throws Exception
     */
    private void sendAndPrinterOrderInC(Order order, Order parentOrder) throws Exception {
        //如果还有未发往厨房的菜，先过滤出来，然后发往厨房
        List<Long> unPrinterOrderItemIdList = orderService.getUnPrinterOrderItemIdList(parentOrder);
        logger.info("un printer order item list is {}", StringUtils.join(unPrinterOrderItemIdList, ","));

        boolean sendToKitchen = false;

        List<OrderItem> parentOrderItemList = orderService.filterOrderItemList(parentOrder, true);

        //全部都没有发送的时候，需要发送
        //更新打印列表
        if (isNotEmpty(unPrinterOrderItemIdList) && (unPrinterOrderItemIdList.size() == getList(parentOrderItemList).size())) {
            sendToKitchen = true;
            logger.info("will send to kitchen");
            DayReportService.getInstance().addSendKitchenNumber(parentOrder, unPrinterOrderItemIdList);

            //如果有优惠券，添加优惠券
            {
                Order cloneOrder = (Order) ClassUtil.clone(parentOrder);
                cloneOrder.setSatisfyAndCutCode(order.getSatisfyAndCutCode());
                PrinterService.getInstance().printerOrderItemList(cloneOrder, unPrinterOrderItemIdList);
            }
            for (Long orderItemId : unPrinterOrderItemIdList) {
                SendPrinter sendPrinter = new SendPrinter();
                sendPrinter.setOrderItemId(orderItemId);
                sendPrinter.setCount(1l);

                if (isEmpty(parentOrder.getSendPrinterList())) {
                    parentOrder.setSendPrinterList(new ArrayList<>());
                }

                parentOrder.getSendPrinterList().add(sendPrinter);
            }

            //打印厨房订单
            OrderOpLogService kitchenOrderOpLogService = OrderOpLogService.getInstance();
            OrderOpLog kitchenOrderOpLog = kitchenOrderOpLogService.content(parentOrder, PRINT_KITCHEN_ORDER);
            kitchenOrderOpLogService.printKitchenOrder(parentOrder, kitchenOrderOpLog);

            //更新订单数据
            commonService.save(parentOrder);
        }

        //判断是否需要打印小票
        Long shopId = parentOrder.getShopId();

        //管理系统配置了发送到厨房之后，自动打印小票
        Long sceneId = orderService.getOrderSceneId(parentOrder);
        Optional<Scene> sceneOptional = commonService.searchOneOptional(sceneId, Scene.class);

        //或者配置了，结账过后客票打印机
        if (sceneOptional.isPresent()) {
            Scene scene = sceneOptional.get();
            boolean canPrint = sendToKitchen && getBoolean(scene.getSendToKitchenAutoPrintOrderInC());

            //优先落单打印
            if (canPrint) {
                printOrderWhenOrderPayedInC(order, parentOrder, printer -> getBoolean(printer.getPrintOrderInC()));
                //c端支付成功后打印小票，打印订单，写入日志
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(order, PRINT_ORDER);
                orderOpLogService.printOrderLog(order, orderOpLog);
            }
        }
    }


    /**
     * C端支付成功之后打印小票
     *
     * @param order
     * @param parentOrder
     * @throws Exception
     */
    private void printOrderWhenOrderPayedInC(Order order, Order parentOrder, Function<Printer, Boolean> filter) throws Exception {
        Long shopId = parentOrder.getShopId();
        List<Printer> printerList = ShopService.getInstance().findPrinterList(shopId);

        Optional<Printer> printerOptional = printerList.stream().filter(p -> filter.apply(p)).findFirst();
        if (printerOptional.isPresent()) {
            Long printerId = printerOptional.get().getId();
            logger.info("c end order and printer id {}", printerId);

            //添加C端的打印机小票的打印机ID
            ThreadContext.put(THREAD_LOCAL_VARIABLE_PRINTER_ORDER_ID_IN_C, String.valueOf(printerId));

            //如果有优惠券，添加优惠券
            Order cloneOrder = (Order) ClassUtil.clone(parentOrder);
            cloneOrder.setSatisfyAndCutCode(order.getSatisfyAndCutCode());
            PrinterService.getInstance().printOrder(cloneOrder, printerId, false, false);
        } else {
            logger.info("can not find the printer id for shop id {}", shopId);
        }
    }

}

class PartOrderPrinterCmd implements Command {
    private static Logger logger = LoggerFactory.getLogger(PartOrderPrinterCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        int userSelectPayType = (Integer) context.get(ContextConstant.USER_SELECT_PAY_TYPE);

        if (!Arrays.asList(USER_SELECT_PAY_TYPE_PART_FOOD, USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON, USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED).contains(userSelectPayType)) {
            logger.info("not part order");
            return false;
        }

        if (order.getPrinterId() == null) {
            logger.info("do not have print id");
            return false;
        }

        //如果是分菜结账的订单，那么打印小票
        Long printerId = order.getPrinterId();
        PrinterService.getInstance().printOrder(order, printerId, false, false);

        //如果开启了结账后客票两次打印
        Long shopId = order.getShopId();
        Shop shop = ShopService.getInstance().findShopById(shopId);
        if (shop.getShopConfig().getPrintOrderTwiceAfterPay()) {
            PrinterService.getInstance().printOrder(order, printerId, false, false);
        }


        //分菜，打印订单写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(order, PRINT_ORDER);
        orderOpLogService.printOrderLog(order, orderOpLog);

        return false;
    }
}

class WebSocketCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(WebSocketCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order parentOrder = (Order) context.get(ContextConstant.PARENT_ORDER);
        Long parentOrderId = parentOrder.getId();
        logger.info("web socket");

        //最后做web socket通知，如果有web socket
        List<WebSocket> webSocketList = WebSocketService.getInstance().getSocket(order.getShopId() + ":" + parentOrderId);

        for (WebSocket webSocket : webSocketList) {
            SocketMessage socketMessage = new SocketMessage();
            socketMessage.setType(Constant.SOCKET_TYPE_ORDER);
            socketMessage.setShopId(order.getShopId());
            socketMessage.setOrderId(order.getId());
            socketMessage.setStatus(0);
            socketMessage.setSatisfyAndCutCode(order.getSatisfyAndCutCode());
            socketMessage.setTakeOutNumber(order.getTakeOutNumber());
            logger.info("will send web socket, {}", socketMessage);
            webSocket.sendMessage(JSONObject.fromObject(socketMessage).toString());
        }

        if (isEmpty(webSocketList)) {
            logger.info("can not find socket");
        }

        return false;
    }
}